#include "gatectrl.h"

int gatectrl_log (int fd, char *buf, size_t count);
void *gatectrl_listen(void *arg);

int gatectrl_allocate(gateCtrlCore *core);
int gatectrl_init_sockets (gateCtrlCore *core);
void gatectrl_free(gateCtrlCore *core);
int gatectrl_notify_gate_status(gateCtrlCore *core);
int gatectrl_notify_new(gateCtrlCore *core);
int gatectrl_device_status(gateCtrlCore *core, int file_desc);
int gatectrl_device_toggle(gateCtrlCore *core, int file_desc);
int gatectrl_client_connect(gateCtrlCore *core);
int gatectrl_client_close(gateCtrlCore *core);

int main(void) {
	/* Our process ID and Session ID */
	pid_t pid, sid;
	
	/* Fork off the parent process */
	pid = fork();

	if (pid < 0) {
		exit(EXIT_FAILURE);
	}
	/* If we got a good PID, then
	we can exit the parent process. */
	if (pid > 0) {
		exit(EXIT_SUCCESS);
	}
	/* Change the file mode mask */
	umask(0);
	
	/* Open logs */
	openlog("GATECTRL", LOG_NDELAY, LOG_DAEMON );

	/* Create a new SID for the child process */
	sid = setsid();
	
	if (sid < 0) {
		syslog (LOG_ERR, "Could not create SID for the child process");
		goto error;
	}

	/* Change the current working directory */
	if ((chdir("/")) < 0) {
		syslog (LOG_ERR, "Could not change the working directory");
		exit(EXIT_FAILURE);
	}

	/* Close out the standard file descriptors */
	close(STDIN_FILENO);
	close(STDOUT_FILENO);
	close(STDERR_FILENO);
	
	/* Gate Controller initialization */
	int port = -1;
	gateCtrlCore *core;
	core = calloc(1 , sizeof(gateCtrlCore));
		
	if ( gatectrl_allocate(core) == -1 )
	{
		syslog (LOG_ERR, "Error allocating memory for core");
		exit(EXIT_FAILURE);
	}
	
	/* Identify the master and create a listener thread */
	if ( findMaster(core->server_ip, &core->master) == -1)
	{
		syslog (LOG_ERR, "Error identifying master");
		goto error;
	}	
	
	syslog (LOG_INFO, "Determined status: Master = %d IP = %s", 
			core->master , core->server_ip);
	
	
	int device;
	device = open ("/dev/gate", O_RDONLY | O_NONBLOCK);
	if (device == -1) {
		syslog (LOG_ERR, "Error opening gate controller");
		goto error;
	}
	
	if ( gatectrl_init_sockets (core) == -1)
	{
		syslog (LOG_ERR, "Error initializing sockets");
		goto error;
	}
	
	
	/* If master start the master thread */
	if (core->master)
	{
		int rc;
		rc=pthread_create(&core->threadserver.thread_id, NULL, start_server, (void *)&core->threadserver);
		
		if (rc)
		{
			syslog (LOG_ERR, "Error creating master thread");
			goto error;
		}
		
		while (core->threadserver.server_ready == false)
		{
			syslog(LOG_DEBUG, "Waiting for server thread to be ready");
		}
		
		
	}
	
	syslog(LOG_DEBUG, "Server ready to receive connections");
	
	/* Create a client connection */
	if ( gatectrl_client_connect(core) == -1)
	{
		syslog (LOG_ERR, "Error initializing sockets");
		goto error;
	}

		
	if ( gatectrl_notify_new(core) == -1 )
	{
		syslog (LOG_ERR, "Error notifying master");
		goto error;			
	}
	
	syslog (LOG_INFO, "Gate controller initialized");
	
	/* The Big Loop */
	while (1) {
			
		/* Checks the status of the gate */
		
		if ( gatectrl_device_status(core, device) == -1 ) 
		{
			syslog (LOG_ERR, "Cannot read gate status");
			goto error;
		}
			
		gatectrl_device_toggle(core, device);
		
		/* Poll simulated gate */
		if (port != core->device)
		{
			gatectrl_notify_gate_status(core);
			port = core->device;
		}	
		
		nanosleep (&req, NULL); 
			
	}
/*@TODO: Use the start/stop to close logs and sockets */
	closelog();
	close(device);
	gatectrl_free(core);
	exit(EXIT_SUCCESS);


error:
	close(device);
	gatectrl_client_close(core);
	gatectrl_free(core);
	exit (EXIT_FAILURE);
	

}

/** This function allocates memory for the 
	core structure **/
int gatectrl_allocate(gateCtrlCore *core) {

	/* Initialize values **/
	core->server_ip = malloc(INET6_ADDRSTRLEN);
	strcpy(core->server_ip, "");
	core->master = false;
	core->device = 0;
	
	if (core == NULL)
	{
		return -1;
	}
	
	return 0;
}

void gatectrl_free(gateCtrlCore *core)
{
	free(core);
}

int gatectrl_init_sockets (gateCtrlCore *core) {
	
	/* Set the master address */
	core->serveraddr.sin_family = AF_INET;
	core->serveraddr.sin_addr.s_addr = inet_addr(core->server_ip);
	core->serveraddr.sin_port = htons(GATECTRL_PORT);
	memset(&(core->serveraddr.sin_zero), '\0', 8);

	/* Set the slave address */
	core->clientaddr.sin_family = AF_INET;
	core->clientaddr.sin_addr.s_addr = inet_addr(core->server_ip);
	core->clientaddr.sin_port = htons(GATECTRL_PORT);
	memset(&(core->clientaddr.sin_zero), '\0', 8);
	
	/* Set thread data */
	core->threadserver.serveraddr=core->serveraddr;
	core->threadserver.listener=-1;
	core->threadserver.server_ready= false;
	core->threadclient.serveraddr=core->clientaddr;	
	core->threadclient.listener=-1;
	core->threadclient.server_ready= false;
	
	/* Initialize counters */
	core->threadserver.num_clients = 0;
		
	return 0;
}

/** This function notifies the master that a new client 
	has arrived **/
int gatectrl_notify_new(gateCtrlCore *core) {
	
	strcpy(core->threadclient.data, CLIENT_NEW);
	strcat(core->threadclient.data, " ");
	strcat(core->threadclient.data, "\0");
	
	int rc, listener;
	core->threadclient.listener=listener;
	rc=pthread_create(&core->threadclient.thread_id, NULL, client_send_message, (void *)&core->threadclient);
		
	if (rc)
		return -1;
	
	return 0;
}

int gatectrl_notify_gate_status (gateCtrlCore *core)
{
	
	// Clear the data 
	strcpy(core->threadclient.data, "");

	//Send port status
	strcat(core->threadclient.data, CLIENT_CHANGED_PORT);
	strcat(core->threadclient.data, " ");
	
	if (core->device)
		strcat(core->threadclient.data, "OPEN");
	else
		strcat(core->threadclient.data, "CLOSED");
	
	strcat(core->threadclient.data, "\0");
	
	int rc, listener;
	core->threadclient.listener=listener;
	rc=pthread_create(&core->threadclient.thread_id, NULL, client_send_message, (void *)&core->threadclient);
		
	if (rc)
		return -1;
	
	return 0;
}

int gatectrl_device_status(gateCtrlCore *core, int file_desc)
{
	int ret_val;

	ret_val = ioctl(file_desc, GATE_STATUS, &core->device);

	if (ret_val < 0) {
		syslog (LOG_ERR, "Cannot get gate status");
		return -1;
	}
	return 0;
}

int gatectrl_device_toggle(gateCtrlCore *core, int file_desc)
{
	int ret_val;
	
	if (core->device)
		ret_val = ioctl(file_desc, GATE_CLOSE, NULL);
	else
		ret_val = ioctl(file_desc, GATE_OPEN, NULL);

	if (ret_val < 0) {
		return -1;
	}
	
	return 0;
}

int gatectrl_client_connect(gateCtrlCore *core)
{
	
	int rc;

	// Create socket 
	if((core->threadclient.client_sd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		syslog(LOG_DEBUG, "Client: Couldn't create a socket");
		return -1;
	}
	
	syslog (LOG_DEBUG,  "Client: Created socket ok");

	rc = connect(core->threadclient.client_sd, (struct sockaddr *)&core->serveraddr, sizeof(core->serveraddr));
	
	if (rc < 0)	
	{
		syslog(LOG_ERR, "Client: Error connecting to master");
		return -1;
	}
	
	return 0;	
}

int gatectrl_client_close(gateCtrlCore *core)
{
	if (core->threadclient.client_sd != -1)
		close (core->threadclient.client_sd);
	else
		return -1;
	
	return 0;
}


